/**
 * main.c - a simple command line tool for audio files.
 * @author  Patrick J Leyden III
 * @version 0.1
 *
 * Plan:
 * Input Data: iiiiiiiiiiiiiii (len: 15)
 * Window: {   } (size: 3)
 * Window Offset: --> (offset: 2)
 *
 * Iterations:
 * 1:
 *        {   }
 *          iiiiiiiiiiiiiii
 *
 * 2:
 *        {   } -->
 *          {   }
 *          iiiiiiiiiiiiiii
 *
 * 3:
 *          {   } -->
 *            {   }
 *          iiiiiiiiiiiiiii
 * 4:
 *            {   } -->
 *              {   }
 *          iiiiiiiiiiiiiii
 * N:
 *                      {   } -->
 *                        {   }
 *          iiiiiiiiiiiiiii
 */

#include <stdio.h>
#include <sndfile.h>
#include <stdlib.h>

#include "datawindow.h"

#define EXIT_CODE_SUCCESS 0
#define EXIT_CODE_BAD_FILENAME -1
#define EXIT_CODE_UNEXPECTED_ERROR -2
#define MAX_FORMAT_STRING_LENGTH 80

void print_file_info(const SF_INFO * p_info);
char * malloc_format_string(int format);
const char * endian_format_string(int format);
const char * type_format_string(int format);
const char * subtype_format_string(int format);

/**
 * Runs the main program.
 * @param argc  the number of arguments
 * @param argv  an array of null terminated strings.
 */
int main(int argc, char ** argv)
{
    SNDFILE * sfp;
    /* Setting the SF_INFO struct values to all zeros is important. */
    SF_INFO info = {0, 0, 0, 0, 0, 0};
    const char * filename = "voice.wav";
    int * p_buffer;
    const sf_count_t num_frames = 100;
    const unsigned int window_size = 4096;
    const unsigned int window_slide = 1024;
    const unsigned int ch_offset = 0;
    int window_cnt = 0; /* For debugging */
    const int window_print = 0; /* For debugging (5 = 2048 to 6144) */
    sf_count_t num_read;
    FILE * fp;
    PL_window * p_window = NULL;

    sfp = sf_open(filename, SFM_READ, &info);
    if (NULL == sfp) {
        fprintf(stderr, "error: failed to open %s\n", filename);
        exit(EXIT_CODE_BAD_FILENAME);
    }

    print_file_info(&info);

    /* For now, print data to file. */
    fp = fopen("plaudex_window.out", "wb");
    if (NULL == fp) {
        fprintf(stderr, "error: cannot open file for writing");
        sf_close(sfp);
        exit(EXIT_CODE_UNEXPECTED_ERROR);
    }

    p_buffer = (int *)malloc(window_slide * info.channels * sizeof(int));
    p_window = create_window(window_size, window_slide);

    do {
        num_read = sf_readf_int(sfp, p_buffer, window_slide);
        if (num_read > 0) {
            int i;

            for (i = 0; i < num_read; ++i) {
                int * p_window_index = (int *)index_from_addr(
                    p_window, p_window->p_head, i);
                *p_window_index = p_buffer[(i * info.channels) + ch_offset];
            }
            /* Add zero padding if needed. */
            for (i = num_read; i < window_slide; ++i)
            {
                int * p_window_index = (int *)index_from_addr(
                    p_window, p_window->p_head, i);
                *p_window_index = 0;
            }

            slide_window(p_window);

            /* This section was added for debugging. */
            /* if (window_cnt == window_print) */
            {
                for (i = 0; i < p_window->length; ++i)
                {
                    int * p_window_index = (int *)index_from_addr(
                        p_window, p_window->p_head, i);
                    fprintf(fp, "%d\n", *p_window_index);
                }
                fprintf(fp, "EOD\n");
            }
            ++window_cnt;

            /* At this point we should have a window filled with data */
        }
    } while (num_read > 0);

    delete_window(p_window);
    free((void *)p_buffer);

    fclose(fp);
    sf_close(sfp);

    return EXIT_CODE_SUCCESS;
}

/** print_file_info function comments go here.
 */
void print_file_info(const SF_INFO * p_info)
{
    if (NULL != p_info) {
        char * format_string = malloc_format_string(p_info->format);
        printf("frames: %d\n"
               "samplerate: %d\n"
               "channels: %d\n"
               "format: %s\n", (int)p_info->frames, p_info->samplerate,
               p_info->channels, format_string);
        free((void *)format_string);
    }
}

char * malloc_format_string(int format)
{
    char * str = (char *)malloc(MAX_FORMAT_STRING_LENGTH);
    snprintf(str, MAX_FORMAT_STRING_LENGTH, "[%s | %s | %s]",
        endian_format_string(format), type_format_string(format),
        subtype_format_string(format));

    return str;
}

const char * endian_format_string(int format)
{
    const char * str = NULL;
    switch (format & SF_FORMAT_ENDMASK) {
        case SF_ENDIAN_FILE:
            str = "Default Endian";
            break;
        case SF_ENDIAN_LITTLE:
            str = "Little Endian";
            break;
        case SF_ENDIAN_BIG:
            str = "Big Endian";
            break;
        case SF_ENDIAN_CPU:
            str = "CPU Endian";
            break;
        default:
            str = "Unknown Endian";
            break;
    }
    return str;
}

const char * type_format_string(int format)
{
    const char * str = NULL;
    switch (format & SF_FORMAT_TYPEMASK) {
        case SF_FORMAT_WAV:
            str = "WAV";
            break;
        case SF_FORMAT_AIFF:
            str = "AIFF";
            break;
        case SF_FORMAT_AU:
            str = "AU";
            break;
        /* TODO: Continue adding types. */
        default:
            str = "Unknown Type";
            break;
    }
    return str;
}

const char * subtype_format_string(int format)
{
    const char * str = NULL;
    switch (format & SF_FORMAT_SUBMASK) {
        default:
            str = "Unknown Subtype";
            break;
    }
    return str;
}
